#pragma once
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <queue>

namespace NSBlockQueue
{
    template <class T, size_t Cap = 5>
    class BlockQueue
    {
    private:
        bool IsFull()
        {
            return _bq.size() == _capacity;
        }

        bool IsEmpty()
        {
            return _bq.empty();
        }

        void LockQueue()
        {
            pthread_mutex_lock(&_mtx);
        }

        void UnlockQueue()
        {
            pthread_mutex_unlock(&_mtx);
        }

        void ComsumerWait()
        {
            pthread_cond_wait(&_cd, &_mtx);
        }

        void ComsumerSignal()
        {
            pthread_cond_signal(&_cd);
        }

        void ProducerWait()
        {
            pthread_cond_wait(&_cb, &_mtx);
        }

        void ProducerSignal()
        {
            pthread_cond_signal(&_cb);
        }

    public:
        BlockQueue()
            : _capacity(Cap)
        {
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_cb, nullptr);
            pthread_cond_init(&_cd, nullptr);
        }

        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mtx);
            pthread_cond_destroy(&_cb);
            pthread_cond_destroy(&_cd);
        }

        void Push(const T &data)
        {
            // 先给队列上锁
            LockQueue();
            while (IsFull())
            {
                ProducerWait();
            }

            _bq.push(data);
            // 唤醒放前放后都可以
            // ComsumerSignal();
            UnlockQueue();
            ComsumerSignal();
        }

        void Pop(T *pdata)
        {
            LockQueue();
            while (IsEmpty())
            {
                ComsumerWait();
            }

            *pdata = _bq.front();
            _bq.pop();
            // ProducerSignal();
            UnlockQueue();
            ProducerSignal();
        }

    private:
        std::queue<T> _bq;
        size_t _capacity;
        pthread_mutex_t _mtx; // 管理临界资源的锁
        pthread_cond_t _cb;   // 管理空白资源的条件变量
        pthread_cond_t _cd;   // 管理数据资源的条件变量
    };
}
